package com.fa4j.third.user.wechat.adapter;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.fa4j.common.base.exception.ServerException;
import com.fa4j.third.base.adapter.AbstractThirdServiceAdapter;
import com.fa4j.third.user.model.*;
import com.fa4j.third.user.service.ThirdUserOauth2Service;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.io.entity.EntityUtils;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * 三方用户 OAuth2 微信适配
 */
@Slf4j
@Setter
public class WechatUserOauth2ServiceAdapter extends AbstractThirdServiceAdapter implements ThirdUserOauth2Service {

    private static final String API_URL = "https://api.weixin.qq.com/sns";

    private String appId;
    private String appSecret;
    private String scope;

    private CloseableHttpClient httpClient;

    /**
     * 初始化
     */
    @Override
    public void init() {
        super.init();
        httpClient = HttpClients.createDefault();
    }

    @Override
    public void close() throws IOException {
        httpClient.close();
    }

    /**
     * 获取授权参数地址
     *
     * @param cmd 参数
     */
    @Override
    public Map<String, String> getAuthorizeParams(ThirdUserGetAuthorizeParamCmd cmd) {
        Map<String, String> result = new HashMap<>();
        String authorizeUrl = "https://open.weixin.qq.com/connect/qrconnect?"
                + "appid=" + appId
                + "&redirect_uri=" + URLEncoder.encode(cmd.getRedirectUri(), StandardCharsets.UTF_8)
                + "&response_type=code"
                + "&scope=" + appSecret
                + "&state=STATE#wechat_redirect";
        result.put("authorize_url", authorizeUrl);
        result.put("appid", appId);
        result.put("redirect_uri", cmd.getRedirectUri());
        result.put("scope", scope);
        result.put("state", "STATE");
        return result;
    }

    /**
     * 获取访问token
     *
     * @param cmd 参数
     */
    @SneakyThrows
    @Override
    public ThirdOauth2UserToken getAccessToken(ThirdUserAccessTokenCmd cmd) {
        String url = API_URL + "/oauth2/access_token" +
                "?appid=" + appId +
                "&secret=" + appSecret +
                "&code=" + cmd.getCode() +
                "&grant_type=authorization_code";
        HttpGet httpGet = new HttpGet(url);
        String result = httpClient.execute(httpGet, classicHttpResponse -> EntityUtils.toString(classicHttpResponse.getEntity()));
        return parseToken(result);
    }

    private ThirdOauth2UserToken parseToken(String result) {
        JSONObject json = toJson(result);
        ThirdOauth2UserToken token = new ThirdOauth2UserToken();
        token.setAppId(appId);
        token.setOpenId(json.getString("openid"));
        token.setUnionId(json.getString("unionid"));
        token.setAccessToken(json.getString("access_token"));
        token.setRefreshToken(json.getString("refresh_token"));
        token.setExpiredTime(LocalDateTime.now().plusSeconds(json.getLong("expires_in")));
        return token;
    }

    /**
     * 刷新访问token
     *
     * @param cmd 参数
     */
    @SneakyThrows
    @Override
    public ThirdOauth2UserToken refreshToken(ThirdUserRefreshTokenCmd cmd) {
        String url = API_URL + "/oauth2/refresh_token" +
                "?appid=" + appId +
                "&grant_type=refresh_token" +
                "&refresh_token=" + cmd.getRefreshToken();
        HttpGet httpGet = new HttpGet(url);
        String result = httpClient.execute(httpGet, classicHttpResponse -> EntityUtils.toString(classicHttpResponse.getEntity()));
        return parseToken(result);
    }

    /**
     * 获取用户信息
     *
     * @param cmd 参数
     */
    @SneakyThrows
    @Override
    public ThirdOauth2UserInfo getUserInfo(ThirdUserGetCmd cmd) {
        String url = API_URL + "/userinfo" +
                "?access_token=" + cmd.getAccessToken() +
                "&openid=" + cmd.getOpenId();
        HttpGet httpGet = new HttpGet(url);
        String result = httpClient.execute(httpGet, classicHttpResponse -> EntityUtils.toString(classicHttpResponse.getEntity()));
        JSONObject json = toJson(result);
        ThirdOauth2UserInfo userInfo = new ThirdOauth2UserInfo();
        userInfo.setAppId(appId);
        userInfo.setOpenId(json.getString("openid"));
        userInfo.setUnionId(json.getString("unionid"));
        userInfo.setNickname(json.getString("nickname"));
        userInfo.setAvatarUrl(json.getString("headimgurl"));
        return userInfo;
    }

    private JSONObject toJson(String result) {
        JSONObject json = JSON.parseObject(result);
        if (json.containsKey("errcode")) {
            throw new ServerException("请求异常,返回:" + result);
        }
        log.info("请求返回:{}", result);
        return json;
    }
}
